const fs = require("fs");
const url = require("url");
const FormData = require("form-data");
const buffer = require("buffer");
const path = require("path");
const util = require("util");
const http = require("http");
const recursive = require("recursive-readdir");
const get_token = require("./get-token");
const upload_stream = require("./v0.1/upload-stream");

const upload_url = "http://upload.qiniu.com/";
const parsed = url.parse(upload_url);


/**
 * 
 * @param {String} full_path must exist
 * @todo 错误处理还没写;必须让所有的part都能成功上传,本函数才算成功
 * @returns {Promise<{ok:Boolean,msg:String,links:String[]}>}
 */
function uploadAsTwoPart(full_path) {
  return new Promise(resolve => {
    fs.stat(full_path, async (err, stats) => {
      if (err) {
        return resolve({
          ok: true,
          msg: `Error when fs.stat() : ${err.message}`,
          links: []
        })
      }
      let size = stats.size;
      let half = parseInt(size / 2);
      let streams = [
        fs.createReadStream(full_path, {
          start: 0,
          end: half
        }),
        fs.createReadStream(full_path, {
          start: half + 1,
          end: size - 1
        })
      ];
      let form_up_promises = streams.map(
        (s, i) => upload_stream.uploadFileStream(s, `part${i}`));
      let o_ps = await Promise.all(form_up_promises);
      if (o_ps.findIndex(o => !o.ok) >= 0) {
        return resolve({
          ok: false,
          msg: `Not all parts are ok`,
          links: []
        })
      }
      resolve({
        ok: true,
        msg: "ok",
        links: o_ps.map(o => o.url)
      })
    })
  })

}


/**
 * 
 * @param {String} full_path  must exist
 * @param {Number} bytes the max size of one part 
 * @returns {Promise<{ok:Boolean,msg:String,links:String[]}>}
 */
function uploadSplitInSize(full_path, bytes = 900 * 1024 * 1024) {
  bytes = parseInt(bytes);
  return new Promise(async resolve => {
    //按循序做异步动作 每一步出错都可以看做失败
    fs.stat(full_path, async (err, stats) => {
      if (err) {
        return resolve({
          ok: false,
          msg: `Error when fs.stat() : ${err.message}`,
          links: []
        })
      }
      let size = stats.size;
      let ok_links = [];
      let start = 0;
      let part_index = 0;
      let g = (function* () {
        while (true) {
          let end = start + bytes - 1;
          if (end > size - 1) {
            end = size - 1;
          }
          let st = fs.createReadStream(full_path, {
            start: start,
            end: end
          });
          yield upload_stream.uploadFileStream(st, `part${part_index}`);
          start = end + 1;
          part_index++;
          if (start > size - 1) {
            return;
          }
        }
      })();
      let it = g.next();
      (function bootstrap() {
        if (!it.done) {
          it.value.then(o => {
            if (!o.ok) {
              return resolve({
                ok: false,
                msg: `error when uploading part ${part_index}:${o.msg}`,
                links: []
              })
            } else {
              ok_links.push(o.url);
              it = g.next();
              bootstrap();
            }
          })
        } else {
          resolve({
            ok: true,
            msg: `ok`,
            links: ok_links
          })
        }
      })();
    })
  });
}


/**
 * 
 * @param {String} full_path must exist
 * @returns {Promise<{ok:Boolean,msg:String,data:{path:String,splitted:Boolean,links:String[],size:Number}}>}
 */
function uploadFileMax1000MB(full_path) {
  return new Promise(resolve => {
    fs.stat(full_path, async (err, stats) => {
      if (err) {
        return resolve({
          ok: false,
          msg: `Error when fs.stat() : ${err.message}`,
          data: {
            path: "",
            splitted: false,
            links: [],
            size: 0
          }
        })
      }
      let size = stats.size;
      if (size <= 1000 * 1024 * 1024) {
        let ext_name = path.extname(full_path);
        let o_up = await upload_stream.uploadFileStream(fs.createReadStream(full_path),
          ext_name ? ext_name.replace(/\./, "") : path.basename(full_path).replace(/\./, ""));
        if (!o_up.ok) {
          return resolve({
            ok: false,
            msg: `Error when upload_stream.uploadFileStream():${o_up.msg}`,
            data: {
              path: "",
              splitted: false,
              links: [],
              size: size
            }
          })
        }
        resolve({
          ok: true,
          msg: "ok",
          data: {
            path: path.basename(full_path),
            splitted: false,
            links: [o_up.url],
            size: size
          }
        })
      } else {
        let o_up = await uploadSplitInSize(full_path, 1000 * 1024 * 1024);
        if (!o_up.ok) {
          return resolve({
            ok: false,
            msg: `Error when uploadSplitInSize():${o_up.msg}`,
            data: {
              path: "",
              splitted: false,
              links: [],
              size: size
            }
          })
        }
        resolve({
          ok: true,
          msg: "ok",
          data: {
            path: path.basename(full_path),
            size: size,
            links: o_up.links,
            splitted: true
          }
        })
      }
    })
  })
}

/**
 * 
 * @param {String} full_path   must exist,must be a dir!
 * @returns {Promise<{ok:Boolean,msg:String,data:Array<{path:String,splitted:Boolean,links:String[],size:Number>}>}
 */
function uploadDirMax1000MB(full_path) {
  return new Promise(resolve => {
    fs.stat(full_path, async (err, stats) => {
      if (err) {
        return resolve({
          ok: true,
          msg: `Error when fs.stat() : ${err.message}`,
          data: []
        })
      }
      if (!stats.isDirectory()) {
        return resolve({
          ok: true,
          msg: `It is not a dir : ${full_path}`,
          data: []
        })
      }
      recursive(full_path, [(file, stats) => {
        return !(stats.isFile() || stats.isDirectory())
      }], async (error, files) => {
        if (error) {
          return resolve({
            ok: false,
            msg: `Error when recursive():${error.message}`,
            data: []
          })
        }
        /**@type {Array<{path:String,splitted:Boolean,links:String[],size:Number}>}*/
        let ok_datas = [];
        let g = (function* () {
          for (let file_path of files) {
            let p = uploadFileMax1000MB(file_path);
            p.then(o => {
              if (!o.ok) {
                resolve({
                  ok: false,
                  msg: `Error when uploadFileMax1000MB(${path.relative(full_path, file_path)}):${o.msg}`,
                  data: []
                })
              } else {
                ok_datas.push({
                  path: path.relative(full_path, file_path),
                  size: o.data.size,
                  splitted: o.data.splitted,
                  links: o.data.links
                })
              }
            });
            yield p;
          }
        })();
        let it = g.next();
        (function bootstrap() {
          if (!it.done) {
            it.value.then(o => {
              if (o.ok) {
                it = g.next();
                setTimeout($ => bootstrap(), 0);
              } else {
                return;
              }
            })
          } else {
            resolve({
              ok: true,
              msg: "ok",
              data: ok_datas
            })
          }
        })();
      })

    })
  })
}

if (!module.parent) {
  // uploadSplitInSize(path.join(
  //   __dirname, "./speedtest.js"
  // ), 1000).then(o => {
  //   debugger
  // })
  // uploadFileMax1000MB(path.join(`F:\\upload-toutiao-picture\\tmp\\2GB.part1.rar`)).then(o => {
  //   debugger
  // })
  // uploadDirMax1000MB(path.join(__dirname, "../../86─不存在的战区─/"))
}

module.exports = {
  uploadSplitInSize: uploadSplitInSize,
  uploadFileMax1000MB: uploadFileMax1000MB,
  uploadDirMax1000MB: uploadDirMax1000MB
}